/* 
 * File:   Servidor.cpp
 * Author: max
 * 
 * Created on October 19, 2012, 12:31 AM
 */

#include <fstream>
#include "server_Servidor.h"
#include "server_FileService.h"
#include "common_RepositorioBuilder.h"
#include "common_MutexScopeLocker.h"

using namespace std;
using namespace TP;
using namespace TP::Comunicacion;
using namespace TP::Archivos;

#define PROCESO_AUTENTICACION 1
#define PROCESO_NOTIFICACION_ABM_ARCHIVOS 2
#define PROCESO_DEPURACION_SESIONES_INACTIVAS 3

Servidor::Servidor(ServerConfFileManager *pConfiguracion){

    this->_configuracion = pConfiguracion;
    this->_conexion = NULL;
    this->_ecoService = NULL;
    this->_fileService = NULL;
    this->_procesoAutenticacion = NULL;
    this->_procesoNotificacionABMArchivos = NULL;
    this->_procesoDepuracionSesionesInactivas = NULL;

}

Servidor::~Servidor() {
    
    // Doy de baja la conexion.
    if (NULL != _conexion)
    {
        delete this->_conexion;
        delete this->_procesoAutenticacion;
        delete this->_ecoService;
        delete this->_fileService;
        delete this->_procesoNotificacionABMArchivos;
        delete this->_procesoDepuracionSesionesInactivas;
    }
    
}

void Servidor::Iniciar() throw (Excepcion&){
    try
    {
        /* Instancia los procesos.
         */
        this->_procesoAutenticacion = new ProcesoAutenticacion(PROCESO_AUTENTICACION, (IAutenticacionManejadorEvento*) this, USUARIOS_DB_RUTA_ARCHIVO);
        this->_procesoNotificacionABMArchivos = new ProcesoNotificacionABMArchivos (PROCESO_NOTIFICACION_ABM_ARCHIVOS, (IAdministradorSesiones*) this, (IAdministradorRepositorioCentral*) this);
        this->_procesoDepuracionSesionesInactivas = new ProcesoDepuracionSesionesInactivas(PROCESO_DEPURACION_SESIONES_INACTIVAS, (IAdministradorSesiones*) this, this->_configuracion->GetTimeoutClientes());
        
        /* Instancia los servicios.
         */
        this->_ecoService = new EcoService();
        this->_fileService = new FileService((IAdministradorRepositorioCentral*) this);
        
        /* Instancia la conexion.
         */
        this->_conexion = new Conexion (_configuracion->GetPuertoServidor(), (IManejadorConexionEvento*)_procesoAutenticacion);
        
        /* Ejecuta la secuencia de inicio.
         */
        this->_conexion->Iniciar();
        this->_procesoAutenticacion->Iniciar();
        this->_procesoNotificacionABMArchivos->Iniciar();
        this->_procesoDepuracionSesionesInactivas->Iniciar();
        

        /* Inicia el repositorio central.
         */
        this->IniciarRepositorioCentral();
        
        /* Inicio de los servicios.
         */
        this->_fileService->Iniciar();
        this->_ecoService->Iniciar();
    }
    catch (Excepcion& pEx)
    {
        pEx.expandirMensaje("No se puede iniciar el sevidor.");
        throw;
    }
}

void Servidor::Finalizar() throw (Excepcion&){
    try
    {
        // Doy de baja la conexion a fin de evitar nuevas sesiones.
        this->_conexion->Finalizar();
        
        this->_procesoNotificacionABMArchivos->Finalizar();
        this->_procesoAutenticacion->Finalizar();
        this->_procesoDepuracionSesionesInactivas->Finalizar();
        
        // Doy de baja las sesiones activas.
        for (vector<Sesion*>::size_type cur = 0; cur < _sesiones.size(); cur++)
        {
            try
            {
                _sesiones[cur]->Finalizar();
            }
            catch (Excepcion& pEx)
            {
//                *(this->log) << pEx.what() << endl;
            }
            delete _sesiones[cur];
        }
        _sesiones.clear();
                
        this->FinalizarRepositorioCentral();
    }
    catch (Excepcion& pEx)
    {
        pEx.expandirMensaje("No se puede finializar correctamente el sevidor.");
        throw;
    }
}

void Servidor::notificarNuevaSesionEvento (Sesion* pSesion){
    
    try
    {
        if (pSesion)
        {
            // Se configuran los manejadores de mensaje de la sesion.
            pSesion->agregarManejadorSesionEvento(this);
            
            pSesion->agregarMensajeEntranteManejador((IMensajeEntranteManejador*)this->_ecoService);
            pSesion->agregarMensajeEntranteManejador((IMensajeEntranteManejador*)this->_fileService);
            
            // Se debe iniciar la escucha de mensajes de la sesion.
            pSesion->Iniciar();
            
            // Se debe registrar la sesion en la lista de sesiones del servidor.
            _sesiones.push_back(pSesion);
        }
    }
    catch (Excepcion& pEx)
    {
        std::cout << "No se puede manejar la nueva sesion.  " << pEx.what() << endl;
    }
}

void Servidor::eventoConexionFinalizada (IEnviante* pEnviante, const string& pMensaje){
    cout << "Servidor.eventoConexionFinalizada no implementado." << endl;
    exit (10);
}

void Servidor::eventoErrorComunicacion (IEnviante* pEnviante, const string& pMensaje ){
    cout << "Servidor.eventoErrorComunicacion no implementado." << endl;
    exit (10);
}

void Servidor::notificarErrorAutenticacionEvento (Sesion* pSesion) {
    try
    {
        this->_conexion->Finalizar();
    }
    catch (Excepcion &ex)
    {
    }
    
    cout << "Servidor.notificarErrorAutenticacionEvento no implementado." << endl;
    exit (10);
}

void Servidor::IniciarRepositorioCentral() throw (Excepcion &){
    
    FileUtils fileUtils;
    
    vector<string> usuarios = this->_procesoAutenticacion->GetBaseDatosUsuarios()->GetUsuarios();
    vector<string>::iterator usuariosIterator = usuarios.begin();
    
    while (usuariosIterator < usuarios.end())
    {
        string path = this->_configuracion->GetPathAU();
        path += SEPARADOR_NODO;
        path += (*usuariosIterator);
        
        if (!fileUtils.DirectorioExistente(path))
        {
            fileUtils.CrearDirectorio(path);
        }
        
        RepositorioBuilder builder;
        Repositorio* repositorio = NULL;
        
        string pathRepositorio = path + SEPARADOR_NODO;
        pathRepositorio += NOMBRE_ARCHIVO_REPOSITORIO_SERIALIZADO;
        
        if (fileUtils.ArchivoExistente(pathRepositorio))
        {
            ifstream file (pathRepositorio.c_str());
            if (file.is_open())
            {
                std::stringstream strstm;
                strstm << file.rdbuf();
                file.close();

                repositorio = Repositorio::Parse(strstm.str());
            }
        }
        else
        {
            repositorio = builder.ConstruirDeRutaDirectorio(path);
        }
        
        if (NULL != repositorio)
        {
            this->_repositorioCentral[(*usuariosIterator)] = repositorio;
        }
        else
        {
            cerr << "No se puede iniciar el repositorio del usuario " << (*usuariosIterator) << endl;
        }
        
        usuariosIterator++;
    }
    
}

void Servidor::FinalizarRepositorioCentral() throw (Excepcion &){
    
    map <string, Repositorio*>::iterator repositorioCentralIterator = this->_repositorioCentral.begin();
    while (repositorioCentralIterator != this->_repositorioCentral.end())
    {
        /* Serializar el repositorio local.
         */
        string path = (*repositorioCentralIterator).second->GetPath() + NOMBRE_ARCHIVO_REPOSITORIO_SERIALIZADO;
        ofstream file (path.c_str(), ios::trunc);

        if (file.is_open())
        {
            file << (*repositorioCentralIterator).second->Serializar();
            file.close();
        }        
        repositorioCentralIterator++;
    }
}

Sesion* Servidor::getSesionPorNombreUsuario(const string &pNombreUsuario){
    
    MutexScopeLocker locker (this->_sesionesMutex);
    
    Sesion* sesion = NULL;
    
    vector<Sesion*>::iterator itSesiones = this->_sesiones.begin();
    while (itSesiones < this->_sesiones.end() && NULL == sesion)
    {
        if ((*itSesiones)->GetUsuario() == pNombreUsuario)
        {
            sesion = (*itSesiones);
        }
        itSesiones++;
    }
    return sesion;
}

Sesion* Servidor::getSesionPrimera(){
    
    MutexScopeLocker locker (this->_sesionesMutex);
    
    if (this->_sesiones.size() > 0)
    {
        return this->_sesiones[0];
    }
    else
    {
        return NULL;
    }
}

Sesion* Servidor::getSesionSiguiente(Sesion* pSesion){
    
    MutexScopeLocker locker (this->_sesionesMutex);
    
    if (NULL == pSesion ) return pSesion;
    
    Sesion* sesion = NULL;
    vector<Sesion*>::iterator itSesiones = this->_sesiones.begin();
    
    while (itSesiones < this->_sesiones.end() && NULL == sesion)
    {
        if ((*itSesiones) == pSesion)
        {
            itSesiones ++;
            if (itSesiones < this->_sesiones.end())
            {
                sesion = (*itSesiones);
            }
        }
        else
        {
            itSesiones++;
        }
    }
    
    return sesion;
}

Repositorio* Servidor::GetRepositorioPorIdentificador (const string &pNombreUsuario){
    MutexScopeLocker locker (this->_repositorioCentralMutex);   
    return this->_repositorioCentral[pNombreUsuario];   
}

vector<string> Servidor::GetIdentificadoresRepositorios (){

    MutexScopeLocker locker (this->_repositorioCentralMutex);   
    
    /* Obtener las claves del diccionario y armar un vector de strings con las claves.
     */
    
    vector<string> claves;
    for(map<string,Repositorio*>::iterator it = this->_repositorioCentral.begin(); it != this->_repositorioCentral.end(); ++it) {
      claves.push_back(it->first);
    }    
    
    return claves;
}